home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / admin / linuxcon.000 / linuxcon / linuxconf-1.6 / dialog / cmdsock.c < prev    next >
C/C++ Source or Header  |  1996-05-12  |  11KB  |  511 lines

  1. #include <stdio.h>
  2. #include <stdarg.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <syslog.h>
  6. #include <errno.h>
  7. #include <netdb.h>
  8. #include <unistd.h>
  9. #include <sys/time.h>
  10. #include <netinet/in.h>
  11. #include <netinet/protocols.h>
  12. #include <limits.h>
  13. #include "cmdsock.h"
  14.  
  15. #if 1
  16. #define DBGALL    1
  17.  
  18. void logevent(const char *fmt, ...)
  19. {
  20.     va_list list;
  21.     va_start (list,fmt);
  22.     char buf[300];
  23.     sprintf (buf,fmt,list);
  24.     syslog (LOG_ERR,buf);
  25.     va_end (list);
  26. }
  27. void logdebug (int level, const char *fmt, ...)
  28. {
  29.     va_list list;
  30.     va_start (list,fmt);
  31.     char buf[300];
  32.     int len = sprintf (buf,"Debug level %d : ",level);
  33.     vsprintf (buf+len,fmt,list);
  34.     syslog (LOG_DEBUG,buf);
  35.     va_end (list);
  36. }
  37.  
  38. #endif
  39.  
  40. /*
  41.     Obtient le port de communication via /etc/service
  42. */
  43. static int cmdsock_getport(const char *service)
  44. {
  45.     int ret = -1;
  46.     struct servent *serv = getservbyname (service,"tcp");
  47.     if (serv == NULL){
  48.         logevent ("Pas de service %s dans /etc/service",service);
  49.     }else{
  50.         ret = ntohs(serv->s_port);
  51.     }
  52.     return ret;
  53. }
  54.  
  55. PRIVATE void CMDSOCK::init(int port)
  56. {
  57.     listen_handle = -1;
  58.     if (port != -1){
  59.         /* Il faut creer le socket original pour la connection */
  60.         char myhost[PATH_MAX];
  61.         gethostname (myhost, sizeof(myhost));
  62.         struct hostent *h = (struct hostent *) gethostbyname(myhost);
  63.         struct sockaddr_in sin;
  64.         sin.sin_family = h->h_addrtype;
  65.         logdebug (DBGALL,"Nom du serveur %s %d %d %d.%d.%d.%d\n"
  66.             ,myhost,h->h_addrtype
  67.             ,h->h_length
  68.             ,(unsigned char)h->h_addr[0]
  69.             ,(unsigned char)h->h_addr[1]
  70.             ,(unsigned char)h->h_addr[2]
  71.             ,(unsigned char)h->h_addr[3]);
  72.         memcpy  (&sin.sin_addr,h->h_addr, h->h_length);
  73.         sin.sin_port = htons(port);
  74.  
  75.         int i;
  76.         for (i=0; i<5; i++){
  77.             listen_handle = socket (AF_INET, SOCK_STREAM, 0);
  78.             sin.sin_addr.s_addr = INADDR_ANY;
  79.             if (listen_handle == -1){
  80.                 logdebug (DBGALL,"listen_handle %d(%s)\n"
  81.                     ,errno
  82.                     ,strerror(errno));
  83.             }else if (bind (listen_handle,(struct sockaddr *) &sin, sizeof (sin)) == -1){
  84.                 logdebug (DBGALL,"bind %d(%s)\n",errno
  85.                     ,strerror(errno));
  86.             }else if (::listen (listen_handle,5) == -1){
  87.                 logdebug (DBGALL,"listen %d(%s)\n",errno
  88.                     ,strerror(errno));
  89.                 break;
  90.             }else{
  91.                 logdebug (DBGALL,"bind ok\n");
  92.                 break;
  93.             }
  94.             close (listen_handle);
  95.             listen_handle = -1;
  96.             if (i < 5) sleep (i*5);
  97.         }
  98.     }
  99. }
  100.  
  101. /*
  102.     Ouverture d'un socket en mode listen pour serveur.
  103. */
  104. PUBLIC CMDSOCK::CMDSOCK(const char *portname)
  105. {
  106.     init (cmdsock_getport(portname));
  107. }
  108. /*
  109.     Ouverture d'un socket en mode listen pour serveur.
  110. */
  111. PUBLIC CMDSOCK::CMDSOCK(int port)
  112. {
  113.     if (port == -1){
  114.         /* #Specification: CMDSOCK / via inetd
  115.             A server using CMDSOCK can be started with inetd.
  116.             We specify -1 as the port number
  117.             CMDSOCK will then use the handle 0 to wait for
  118.             connections.
  119.         */
  120.         listen_handle = 0;
  121.     }else{
  122.         init (port);
  123.     }
  124. }
  125.  
  126. PUBLIC CMDSOCK::~CMDSOCK()
  127. {
  128.     if (listen_handle != -1){
  129.         SOCK_INFO *pt = inf;
  130.         for (int i=0; i<nbcli; i++,pt++) close (pt->handle);
  131.         close (listen_handle);
  132.     }
  133. }
  134. /*
  135.     Retourne != 0 si le serveur est correctement initialise
  136. */
  137. PUBLIC int CMDSOCK::is_ok()
  138. {
  139.     return listen_handle != -1;
  140. }
  141. /*
  142.     Ferme la connexion d'un client.
  143. */
  144. PUBLIC void CMDSOCK::closecli(int fd)
  145. {
  146.     close (fd);
  147.     int dst = 0;
  148.     SOCK_INFO *pt = inf;
  149.     for (int i=0; i<nbcli; i++, pt++){
  150.         if (pt->handle != fd){
  151.             inf[dst++] = inf[i];
  152.         }
  153.     }
  154.     nbcli = dst;
  155. }
  156.  
  157. /*
  158.     Ajoutte un handle de plus au serveur avec gestion de timeout.
  159.  
  160.     Le timeout est en secondes.
  161.  
  162.     La connexion de ce handle a ΘtΘ realisΘe par l'appelant. On
  163.     retrouve cette situation lorsqu'un serveur dΘmarre lui-mΩme
  164.     les clients ou entre en contact avec un autre serveur.
  165. */
  166. PUBLIC void CMDSOCK::addcli (int fd, int timeout)
  167. {
  168.     SOCK_INFO *pt = inf + nbcli++;
  169.     pt->handle = fd;
  170.     pt->idle.set = timeout * 1000000;
  171.     pt->idle.cur = 0;
  172.     pt->actif = 0;
  173. }
  174.  
  175. /*
  176.     Ajoutte un handle de plus au serveur.
  177. */
  178. PUBLIC void CMDSOCK::addcli (int fd)
  179. {
  180.     addcli (fd,0);
  181. }
  182.  
  183.  
  184. /*
  185.     Attend de l'activite sur un handle parmi plusieurs ou une connection
  186.     sur un socket.
  187.  
  188.     Return -1 if any error.
  189.     Return 0 if the timeout was met.
  190.     Return  > 0 if something is available (or simply a new connexion).
  191.     In that case, readnext() should be called to get some work.
  192. */
  193. PUBLIC int CMDSOCK::listen (
  194.     long timeout)        // Microseconds
  195.                 // -1 = no timeout
  196.                 //  0 = pollmode
  197. {
  198.     fd_set set;
  199.     FD_ZERO(&set);
  200.     int maxhd = 0;
  201.     SOCK_INFO *pt = inf;
  202.     for (int i=0; i<nbcli; i++,pt++){
  203.         int handle = pt->handle;
  204.         pt->actif = 0;
  205.         FD_SET (handle,&set);
  206.         if (handle > maxhd) maxhd = handle;
  207.     }
  208.     FD_SET (listen_handle,&set);
  209.     if (listen_handle > maxhd) maxhd = listen_handle;
  210.     struct timeval timeo;
  211.     if (timeout > 1000000){
  212.         timeo.tv_sec  = timeout/1000000;
  213.         timeo.tv_usec = timeout%1000000;
  214.     }else{
  215.         timeo.tv_sec  = 0;
  216.         timeo.tv_usec = timeout;
  217.     }
  218.     fd_set spcset;
  219.     spcset = set;
  220.     struct timeval *pttimeo = NULL;
  221.     if (timeout != -1) pttimeo = & timeo;
  222.     int sel = select (maxhd+1,&set,NULL,&spcset,pttimeo);
  223.     actif = 0;
  224.     int ret = 0;
  225.     if (sel > 0){
  226.         if (FD_ISSET(listen_handle,&set) != 0){
  227.             char sacc[100];
  228.             int size=100;
  229.             int fd = accept (listen_handle,(struct sockaddr *)sacc
  230.                 ,&size);
  231.             addcli (fd);
  232.         }
  233.         pt = inf;
  234.         for (i=0; i<nbcli; i++,pt++){
  235.             int handle = pt->handle;
  236.             if (FD_ISSET(handle,&spcset)!=0){
  237.                 logdebug (DBGALL,"client %d dans spcset\n",handle);
  238.             }
  239.             if (FD_ISSET(handle,&set)){
  240.                 pt->actif = 1;
  241.                 pt->idle.cur = 0;
  242.             }else{
  243.                 pt->idle.cur += timeout;
  244.             }
  245.         }
  246.         ret = 1;
  247.     }else{
  248.         // Aucune connexion active, on augmente les timeout
  249.         pt = inf;
  250.         for (i=0; i<nbcli; i++,pt++){
  251.             pt->idle.cur += timeout;
  252.         }
  253.     }
  254.     return ret;
  255. }
  256.  
  257. /*
  258.     Retourne le nombre de connexion courramment monitorΘ.
  259.     Cette fonction est gΘnΘralement utilisΘe pour dΘcider si on doit
  260.     terminer le service
  261. */
  262. PUBLIC int CMDSOCK::getnbcli()
  263. {
  264.     return nbcli;
  265. }
  266. /*
  267.     Lit une transaction du prochain client actif.
  268.     Retourne 0 si le client a fermΘ la connexion.
  269.     Retourne -1 s'il n'y en a plus.
  270.     Retourne le nombre de byte placΘ dans buf. fd contiendra
  271.     le handle du client qui sera utilisΘ pour transmettre
  272.     la reponse.
  273. */
  274. PUBLIC int CMDSOCK::readnext(void *buf, int size, int &cli)
  275. {
  276.     int ret = -1;
  277.     SOCK_INFO *pt = inf + actif;
  278.     for ( ; actif < nbcli; actif++, pt++){
  279.         if (pt->actif){
  280.             cli = pt->handle;
  281.             logdebug (DBGALL,"Transaction du client %d\n",cli);
  282.             int nb = read (cli,buf,size);
  283.             if (nb > 0){
  284.                 ret = nb;
  285.                 actif++;
  286.             }else{
  287.                 logdebug (DBGALL,"Client %d a ferme la connexion\n",cli);
  288.                 closecli (cli);
  289.                 ret = 0;
  290.             }
  291.             break;
  292.         }else if (pt->idle.set > 0){
  293.             if (pt->idle.cur > pt->idle.set){
  294.                 cli = pt->handle;
  295.                 ret = 0;
  296.                 closecli (cli);
  297.                 syslog (LOG_INFO,"Stale connexion %d, closing",cli);
  298.                 break;
  299.             }
  300.         }
  301.     }
  302.     return ret;
  303. }
  304. #if 0
  305. /*
  306.     Etablie la connexion avec un serveur.
  307.     Retourne -1 si erreur.
  308. */
  309. int cmdsock_connect (const char *servname, int port, int nbretry)
  310. {
  311.     int ret = -1;
  312.     struct hostent    *h = (struct hostent *) gethostbyname(servname);
  313.     if (h == NULL){
  314.         logevent ("Pas de serveur \"%s\" dΘfini",servname);
  315.     }else{
  316.         struct sockaddr_in sin;
  317.         sin.sin_family = h->h_addrtype;
  318.         memcpy  (&sin.sin_addr,h->h_addr, h->h_length);
  319.         sin.sin_port = htons(port);
  320.         for (int i=0; i<nbretry; i++){        
  321.             int s;
  322.             if ((s = socket (AF_INET, SOCK_STREAM, 0)) >= 0) {
  323.                 logdebug (DBGALL,"avantconnect %d\n",s);
  324.                 if (connect (s, (struct sockaddr *) &sin, sizeof (sin))
  325.                     == -1){
  326.                     if (i==0){
  327.                         logdebug (DBGALL,"Ne peut realiser connect (%s)\n"
  328.                             ,strerror(errno));
  329.                     }
  330.                     close (s);
  331.                     sleep(1);
  332.                 }else{
  333.                     ret = s;
  334.                     break;
  335.                 }
  336.             }else{
  337.                 logdebug (DBGALL,"socket");
  338.             }
  339.         }
  340.     }
  341.     return ret;
  342. }
  343. /*
  344.     Etablie la connexion avec un serveur.
  345.     Retourne -1 si erreur.
  346. */
  347. int cmdsock_connect (const char *servname, const char *portname, int nbretry)
  348. {
  349.     int port = cmdsock_getport(portname);
  350.     int ret = -1;
  351.     if (port != -1){
  352.         ret = cmdsock_connect (servname,port,nbretry);
  353.     }
  354.     return ret;
  355. }
  356. /*
  357.     Etablie la connexion avec le serveur.
  358.     Retourne -1 si erreur.
  359. */
  360. int cmdsock_connect ()
  361. {
  362.     /* #Specification: serveur / location
  363.         Les clients du serveur assume qu'il execute toujours
  364.         sur ngdhost. Il s'agit donc de mettre le bon alias dans
  365.         /etc/hosts ou ailleurs.
  366.     */
  367.     /* #Specification: serveur / port de communication
  368.         La communication se fait par socket. Le port de communication
  369.         est controlΘ par /etc/service. On y retrouvera la ligne
  370.         suivante
  371.  
  372.         ngd    xxx/tcp
  373.  
  374.         xxx sera un numΘro de port non utilisΘ < 1024.
  375.     */
  376.     return cmdsock_connect ("ngdhost","ngd",10);
  377. }
  378. /*
  379.     Envoie un seul message via un socket. Le socket est Θtablie
  380.     α chaque transaction.
  381.  
  382.     Retourne -1 si erreur.
  383. */
  384. int cmdsock_sendmsg (char *retbuf, int size, const char *ctl, ...)
  385. {
  386.     int ret = -1;
  387.     int fd = cmdsock_connect();
  388.     if (fd != -1){
  389.         va_list list;
  390.         va_start (list,ctl);
  391.         char buf[1000];
  392.         int nb = vsprintf (buf,ctl,list);
  393.         buf[nb] = '\0';
  394.         write (fd,buf,nb);
  395.         int readnb = read (fd,retbuf,size);
  396.         if (readnb > 0){
  397.             retbuf[readnb] = '\0';
  398.             ret = 0;
  399.         }
  400.         close (fd);
  401.     }
  402.     return ret;
  403. }
  404. /*
  405.     Retourne 1 s'il y a quelques chose a lire sur fd.
  406.     Retourne 0 si le timeout est ΘcoulΘ.
  407.     Retourne -1 s'il y a un erreur.
  408. */
  409. int cmdsock_wait (int fd, long timeout)
  410. {
  411.     int tb[1];
  412.     tb[0] = fd;
  413.     return cmdsock_wait (1,tb,tb,timeout);
  414. }
  415.  
  416. /*
  417.     Retourne le nombre de handle prΩt pour une lecture (data disponible)
  418.     Retourne 0 si le timeout est ΘcoulΘ.
  419.     Retourne -1 s'il y a un erreur.
  420. */
  421. int cmdsock_wait (int nbfds, int fds[],int ready[], long timeout)
  422. {
  423.     fd_set set;
  424.     FD_ZERO (&set);
  425.     int maxfd = 0;
  426.     int i;
  427.     for (i=0; i<nbfds; i++){
  428.         int fd = fds[i];
  429.         FD_SET (fd,&set);
  430.         if (fd > maxfd) maxfd = fd;
  431.     }
  432.     struct timeval timeo;
  433.     if (timeout > 1000000){
  434.         timeo.tv_sec  = timeout/1000000;
  435.         timeo.tv_usec = timeout%1000000;
  436.     }else{
  437.         timeo.tv_sec  = 0;
  438.         timeo.tv_usec = timeout;
  439.     }
  440.     int ret = select (maxfd+1,&set,NULL,NULL,timeout == -1 ? NULL : &timeo);
  441.     if (ret > 0){
  442.         ret = 0;
  443.         for (i=0; i<nbfds; i++){
  444.             int fd = fds[i];
  445.             if (FD_ISSET (fd,&set)) ready[ret++] = fd;
  446.         }
  447.     }
  448.     return ret;
  449. }        
  450. #endif
  451.  
  452. #ifdef TEST
  453. static void usage_test()
  454. {
  455.     fprintf (stderr,
  456.         "x serv ou x client\n"
  457.         "Le serveur retourne chaque ligne que le client envoie\n"
  458.         "avec un petit ajout\n"
  459.         );
  460. }
  461.  
  462. int main (int argc, char *argv[])
  463. {
  464.     if (argc == 2){
  465.         if (strncmp(argv[1],"ser",3)==0){
  466.             CMDSOCK cmd;
  467.             if (cmd.is_ok()){
  468.                 while (1){
  469.                     if (cmd.listen (3000000) != -1){
  470.                         char buf[1000];
  471.                         int nb;
  472.                         int cli;
  473.                         while ((nb=cmd.readnext(buf,sizeof(buf),cli))!=-1){
  474.                             if (nb == 0){
  475.                                 printf ("cli %d a ferme la connexion\n",cli);
  476.                             }else{
  477.                                 char repond[2000];
  478.                                 buf[nb] = '\0';
  479.                                 printf ("recoit requete :%s:\n",buf);
  480.                                 nb = sprintf (repond,"RECU ceci <%s>",buf);
  481.                                 write (cli,repond,nb);
  482.                             }
  483.                         }
  484.                     }
  485.                 }
  486.             }
  487.         }else if (strncmp(argv[1],"cli",3)==0){
  488.             while (1){
  489.                 char buf[200];
  490.                 printf ("Entrer n'importe quoi "); fflush (stdout);
  491.                 fgets(buf,sizeof(buf)-1,stdin);
  492.                 char retbuf[200];
  493.                 if (cmdsock_sendmsg (retbuf,sizeof(retbuf),"%s",buf)==-1){
  494.                     fprintf (stderr,"Ne peut transmettre au serveur\n");
  495.                 }else{
  496.                     printf ("Recoit :%s:\n",retbuf);
  497.                 }
  498.             }
  499.         }else{
  500.             usage_test();
  501.         }
  502.     }else{
  503.         usage_test();
  504.     }
  505.     return 0;
  506. }
  507.  
  508.  
  509. #endif
  510.  
  511.